package com.chapter20.demo5;


import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.*;

/**
 * 批量执行任务：
 * CompletionService接口在内部使用队列保存每个任务的Future对象，保存的
 * 顺序是任务执行完成的顺序，哪个任务先执行完毕，该任务就将被先加入到队列中。
 *
 * Future<V> take() throws InterruptedException
 * 获取并删除队列中下一个已完成任务的Future对象，如果不存在，则一直等待。
 *
 * 我们在使用submit方法提交任务后，可以调用poll或者take方法从队列中获取已完成任务的Future对象，不需要直接使用submit方法返回的Future对象。
 * 这样就将异步任务的产生与已完成任务的结果处理分离开了，生产者提交任务以供执行，消费者接受已完成的任务，并按完成的顺序处理结果。
 * 例如，CompletionService可用于管理异步I/O，在异步I/O中，执行读取的任务在程序的某个地方提交，在读取完成时在程序的另一个地方对结果进行处理。
 * CompletionService依赖于单独的执行器来实际执行任务，
 * ExecutorCompletionService是该接口的实现类，
 * 在构造ExecutorCompletionService对象时，需要传递一个执行器对象作为参数。
 */
public class UserCompletionService {
    private static class  CallableSumTask implements Callable<Long>{
        private long start;
        private long end;

        public CallableSumTask(long start, long end) {
            this.start = start;
            this.end = end;
        }

        @Override
        public Long call() throws Exception {
            System.out.println(String.format("Start:%d End:%d",start,end));
            long sum =0L;
            for (long i=start;i<=end;i++){
                sum=sum+i;
            }
            return sum;
        }
    }


    //计算1到1000万的整数的和，将计算过程分成5个任务同时执行
    public static void main(String[] args) {
        final long Max =10000000;
        ExecutorService exec = Executors.newFixedThreadPool(5);
        Collection<CallableSumTask> cl = new ArrayList<>();

        for (int i=0;i<5;i++){
            //将任务添加到cl (ArrayList)对象中的
            cl.add(new CallableSumTask(i*Max/5+1,(i+1)*Max/5));
        }

        //CompletionService依赖于单独的执行器来实际执行任务，ExecutorCompletionService是该接口的实现类，
        // 在构造ExecutorCompletionService对象时，需要传递一个执行器对象作为参数。
        ExecutorCompletionService<Long> cs = new ExecutorCompletionService<>(exec);

        //使用submit方法提交cl中的所有任务
        cl.forEach(cs::submit);

        long sum =0L;

       try {
           for (int i=0;i< cl.size();i++){
               //调用poll或者take方法从队列中获取已完成任务的Future对象，不需要直接使用submit方法返回的Future对象。
               Long result = cs.take().get();

               if(result!=null){
                   sum+=result;
                   System.out.println("result:"+result);
               }
           }
           System.out.println("sum:"+sum);
       }
       catch (InterruptedException |ExecutionException  e) {
           throw new RuntimeException(e);
       }
       finally {
           exec.shutdown();
       }


    }
}
